<script>
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-param-reassign */
/* eslint-disable no-nested-ternary */
/* eslint-disable no-unused-expressions */
/* eslint-disable consistent-return */

import { Table, TableColumn } from 'element-ui';
import { set, get, cloneDeep } from 'lodash';
import noSearchResult from '@assets/img/pc-other_wcxdjg_icon.png';
import noMessage from '@assets/img/pc-other_zwsj_icon.png';
import mixinOptionExtensions from './mixin-package-option';
import {
  TYPES,
  SLOT_DEFAULT,
  SLOT_HEADER,
  HeaderCellClassName,
  // RowClassName,
  disableMoveTableArr,
  PAGE_NUMBER,
  PAGINATION_LAYOUT,
  toCamelCase,
} from './const';

let len = 0;
// 定义表头class类名

export default {
  name: 'MEditable',
  components: {
    'el-table': Table,
    'el-table-column': TableColumn,
  },
  directives: {
    drag: {
      bind(el, binding) {
        // 如果没有配置custom-attr属性，则不允许移动
        if (!binding.expression) {
          return;
        }
        // const classNameArr = ['gutter'];
        setTimeout(() => {
          const tbodyBox = el.querySelector('thead');
          el.querySelectorAll(
            `.el-table__header-wrapper tr .${HeaderCellClassName}`
          ).forEach((item, index) => {
            item.setAttribute('draggable', true);
            item.setAttribute('index', index);
          });
          let curIndex = 0;
          let toIndex = 0;
          tbodyBox.addEventListener(
            'dragstart',
            (e) => {
              curIndex = e.target.getAttribute('index');
            },
            false
          );
          tbodyBox.addEventListener(
            'dragover',
            (e) => {
              toIndex = e.path
                .filter((item) => item.nodeName === 'TH')[0]
                .getAttribute('index');
            },
            false
          );
          tbodyBox.addEventListener(
            'dragend',
            () => {
              binding.value({ curIndex, toIndex });
            },
            false
          );
        }, 0);
      },
    },
  },
  mixins: [mixinOptionExtensions],
  props: {
    columns: {
      type: Array,
      default: () => [],
    },
    data: {
      type: Array,
      default: () => [],
    },
    editableMethod: {
      type: Function,
      default: () => {},
    },
    payload: {
      type: Object,
      default: () => {},
    },
    needPagination: {
      type: Boolean,
      default: false,
    },
    // 这里做的兼容是为了某种特殊的情况，这种特殊情况极少，或者是十分不允许的。
    staticStore: {
      default: null,
      type: Object,
    },
    emptyType: {
      default: 0,
      type: Number,
    },
  },
  data() {
    return {
      tableData: [],
      tablePager: { page: 1, size: PAGE_NUMBER[0], total: 0 },
      store: [],
      spanArr: [],
      spanObject: {},
      customSpanMethod: () => {},
    };
  },
  computed: {
    storeConfig() {
      if (this.staticStore) {
        return this.staticStore.state.tableHeadConfig;
      }
      return this.$store.state.tableHeadConfig;
    },
    customColumns() {
      // this.columns
      // 如果存在，则重新定义顺序，如果不存在，则直接返回当前columns
      if (this.storeConfig && this.storeConfig[this.$attrs['custom-attr']]) {
        const jsonArr = this.storeConfig[this.$attrs['custom-attr']];
        return jsonArr.map((item) => this.columns[item]);
      }
      return this.setExcessDefaultAttr(this.columns);
    },
  },
  watch: {
    data: {
      handler(val) {
        this.tableData = val;
      },
      immediate: true,
    },
    columns: {
      handler: 'rowspan',
      immediate: true,
    },
    payload: {
      handler(val) {
        if (val !== undefined && val.content !== undefined) {
          this.tableData = val.content;
          this.tablePager = {
            size: val.size,
            page: val.number,
            total: val.totalElements,
          };
        }
      },
      immediate: true,
    },
  },
  mounted() {
    this.customSpanMethod =
      this.$attrs['span-method'] ||
      this.$attrs.spanMethod ||
      this.arraySpanMethod;
  },
  methods: {
    setExcessDefaultAttr(columns) {
      return columns.map((item) => {
        const stItem = item;
        if (stItem.children && stItem.children.length) {
          stItem.children = this.setExcessDefaultAttr(stItem.children);
        }
        const storageItem = stItem;
        return storageItem.align ||
          ['selection'].includes(storageItem.type || storageItem.prop)
          ? storageItem
          : {
              ...storageItem,
              'show-overflow-tooltip': true,
              align: 'center', // 所有单元格全部居中，type为selection除外
            };
      });
    },
    // 是否可以执行表头移动 ,'custonAttr', 'fixed'是属性
    checkTableHead(curIndex, toIndex) {
      let flag = true;
      const lastColumns = this.customColumns;
      // 选中的对象
      const curObj = lastColumns[curIndex];
      if (
        disableMoveTableArr.includes(curObj.type) ||
        curObj.customAttr ||
        curObj.fixed
      ) {
        flag = false;
      }
      // 目标对象
      const toObj = lastColumns[toIndex];
      if (
        disableMoveTableArr.includes(toObj.type) ||
        toObj.customAttr ||
        toObj.fixed
      ) {
        flag = false;
      }
      return flag;
    },
    // 更改表头顺序
    changeTableHeadConfig({ curIndex, toIndex }) {
      // 获取表头，查看表头是否可以更改
      if (this.checkTableHead(curIndex, toIndex)) {
        // 标识位置的更改
        let config;
        if (this.staticStore) {
          config = this.staticStore.state.tableHeadConfig;
        } else {
          config = this.$store.state.tableHeadConfig;
        }
        let arrIndex;
        if (config && config[this.$attrs['custom-attr']]) {
          arrIndex = cloneDeep(config[this.$attrs['custom-attr']]);
        } else {
          arrIndex = new Array(this.customColumns.length)
            .fill('')
            .map((item, index) => index);
        }
        arrIndex.splice(toIndex, 0, ...arrIndex.splice(curIndex, 1));
        if (this.staticStore) {
          this.staticStore.commit('tableHeadConfig', {
            json: arrIndex,
            path: this.$attrs['custom-attr'],
          });
        } else {
          this.$store.commit('tableHeadConfig', {
            json: arrIndex,
            path: this.$attrs['custom-attr'],
          });
        }
      }
    },
    renderColumn(h, data = {}) {
      return data.columns.map((col) => {
        // todo: 待优化
        len = col.children ? len : len + 1; // 用于计算 column 的 index
        const columnIndex = len - 1; // 从0开始，所以减1

        const filterData = {
          props: col,
        };
        const scopedSlot =
          this.$scopedSlots[SLOT_DEFAULT + col.type] ||
          this.$scopedSlots[SLOT_DEFAULT + col.prop];

        if ((!col.type || TYPES.indexOf(col.type) < 0) && !scopedSlot) {
          filterData.scopedSlots = {
            default: (scope) => {
              const config =
                (data.editableMethod &&
                  data.editableMethod({
                    row: scope.row,
                    column: scope.column,
                    rowIndex: scope.$index,
                    columnIndex,
                  })) ||
                col.render;

              const renderElement =
                (typeof config === 'object' &&
                  (config.$component || config.$type)) ||
                'el-input';
              // eslint-disable-next-line no-nested-ternary
              return config
                ? h(
                    renderElement,
                    {
                      props: { ...config.$el, value: get(scope.row, col.prop) },
                      on: {
                        input: (val) => {
                          // 处理空值时修改很慢的问题
                          // eslint-disable-next-line no-unused-expressions
                          get(scope.row, col.prop) === undefined
                            ? this.$set(scope.row, col.prop, val) // todo: 这里存在bug, 如果 prop 为 . 拼接的 key
                            : set(scope.row, col.prop, val);
                        },
                        change: (val) => {
                          this.handleChange({
                            prop: col.prop,
                            value: val,
                            row: scope.row,
                            column: scope.column,
                            rowIndex: scope.$index,
                            columnIndex,
                          });
                        },
                      },
                    },
                    [
                      // eslint-disable-next-line consistent-return
                      (() => {
                        const optType =
                          config.$type && config.$type.indexOf('el-') === 0
                            ? config.$type.slice(3)
                            : config.$type;
                        const optRenderer =
                          optType && this[`${toCamelCase(optType)}_opt`];
                        if (
                          typeof optRenderer === 'function' &&
                          Array.isArray(config.$options)
                        ) {
                          return config.$options.map(optRenderer);
                        }
                      })(),
                    ]
                  )
                : col.formatter
                ? col.formatter(
                    scope.row,
                    scope.column,
                    get(scope.row, col.prop),
                    scope.$index
                  )
                : get(scope.row, col.prop);
            },
          };
        }
        if (scopedSlot) {
          filterData.scopedSlots = {
            default: scopedSlot,
          };
        }
        if (this.$scopedSlots[SLOT_HEADER + col.prop]) {
          filterData.scopedSlots.header =
            this.$scopedSlots[SLOT_HEADER + col.prop];
        }
        return h(
          'el-table-column',
          filterData,
          col.children && [
            this.renderColumn(h, {
              columns: col.children,
              editableMethod: this.editableMethod,
            }),
          ]
        );
      });
    },
    rowspan() {
      const data = this.tableData;

      const getSpanObject = (arr, con = {}) =>
        arr.reduce((sum, col) => {
          if (col.children && col.children.length) {
            getSpanObject(col.children, sum);
            return sum;
          }
          // 如果这一列没有合并字段，则不去进行后续的计算，直接返回当前的结果
          if (!col.isMergeCell) {
            return sum;
          }

          // eslint-disable-next-line no-param-reassign
          sum[col.prop] = [];

          let position;
          const spanArr = sum[col.prop];

          data.forEach((item, index) => {
            if (index === 0) {
              spanArr.push(1);
              position = 0;
            } else if (data[index][col.prop] === data[index - 1][col.prop]) {
              spanArr[position] += 1;
              spanArr.push(0);
            } else {
              spanArr.push(1);
              position = index;
            }
          });
          return sum;
        }, con);

      this.spanObject = getSpanObject(this.columns);
    },
    // eslint-disable-next-line consistent-return
    arraySpanMethod({ column, rowIndex }) {
      const spanArr = this.spanObject[column.property];
      if (spanArr) {
        const rowItem = spanArr[rowIndex];
        const colItem = rowItem > 0 ? 1 : 0;
        return {
          rowspan: rowItem,
          colspan: colItem,
        };
      }
      return {
        rowspan: 1,
        colspan: 1,
      };
    },
    handleChange(data) {
      const index = this.store.findIndex(
        (item) =>
          item.rowIndex === data.rowIndex &&
          item.columnIndex === data.columnIndex
      );
      this.$emit('cell-change', data);
      if (index > -1) {
        this.store.splice(index, 1, data);
        return;
      }
      this.store.push(data);
    },
    setCellValue(key, rowIndex, value) {
      const row = this.tableData[rowIndex];
      set(row, key, value);
    },
    clearStore() {
      this.store = [];
    },
    getValue() {
      return this.store;
    },
  },
  render(h) {
    len = 0; // 初始化值

    const slots = Object.keys(this.$slots)
      .reduce((arr, key) => arr.concat(this.$slots[key]), [])
      // 手动更正 context
      .map((vnode) => {
        const varVnode = vnode;
        // eslint-disable-next-line no-underscore-dangle
        varVnode.context = this._self;
        return varVnode;
      });
    const columns = this.renderColumn(h, {
      // 优先使用自定义表头
      columns: this.customColumns,
      editableMethod: this.editableMethod,
    });

    const emptyBox = h(
      'div',
      {
        slot: 'empty',
        style: {
          lineHeight: 'initial',
        },
      },
      [
        h('img', {
          attrs: {
            src: this.emptyType ? noSearchResult : noMessage,
          },
          style: {
            width: '172px',
            height: '100px',
          },
        }),
        h(
          'p',
          {
            style: {
              marginTop: '17px',
              color: '#999',
            },
          },
          this.emptyType ? '未查询到结果' : '暂无数据'
        ),
      ]
    );
    const elemTable = h(
      'el-table',
      {
        ref: 'table',
        props: {
          ...this.$attrs,
          data: this.tableData,
          // rowClassName: RowClassName,
          border:
            this.$attrs.border === null || this.$attrs.border === undefined
              ? true
              : this.$attrs.border,
          headerCellClassName: HeaderCellClassName,
          spanMethod: this.customSpanMethod,
        },
        directives: [
          {
            name: 'drag',
            value: this.changeTableHeadConfig,
            expression: !!this.$attrs['custom-attr'],
          },
        ],
        on: this.$listeners,
      },
      [columns.concat(slots), emptyBox]
    );

    const elemPagination = h('div', { attrs: { class: 'pagination' } }, [
      h('el-pagination', {
        props: {
          layout: PAGINATION_LAYOUT,
          pageSize: this.tablePager.size,
          total: this.tablePager.total,
          currentPage: this.tablePager.page,
          pageSizes: PAGE_NUMBER,
        },
        attrs: { align: 'right' },
        on: {
          'size-change': (pageSize) => {
            this.tablePager.size = pageSize;
            this.tablePager.page = 1;
            this.$emit('reload', {
              number: this.tablePager.page,
              size: this.tablePager.size,
              totalElements: this.tablePager.total,
            });
          },
          'current-change': (curPage) => {
            this.tablePager.page = curPage;
            this.$emit('reload', {
              number: this.tablePager.page,
              size: this.tablePager.size,
              totalElements: this.tablePager.total,
            });
          },
        },
      }),
    ]);

    const subelem = [];
    subelem.push(elemTable);
    // // qiyu 2020-3-19 此处应取当前可选分页的最小值而非当前分页大小，否则会出现切成100条/页后，分页条消失无法再调整的问题
    // if (this.tablePager.total > PAGE_NUMBER[0]) {
    //   subelem.push(elemPagination);
    // }
    if (this.needPagination) {
      subelem.push(elemPagination);
    }
    return h('div', { class: 'm-editable' }, subelem);
  },
};
</script>
<style lang="less">
.m-editable {
  display: flex;
  flex: 1;
  flex-direction: column;
  overflow: hidden;
  .el-table {
    // .cell{
    //   line-height: 24px;
    //   text-align: left;
    //   display: inline-block;
    //   width: auto;
    // }
    // // 这里是因为冲突，居中的方案会使.cell变成inline-block。导致tooltip失效，所以加个这个
    // .cell.el-tooltip{
    //   display: block;
    // }
    .el-table__empty-block {
      height: 100%;
    }
    .editable-header-cell-class {
      padding: 7px 0;
      .cell {
        font-weight: @boldFontWeight;
      }
    }
    .el-table__row {
      td {
        padding: 2px 0;
        height: 38px;
      }
    }
    // th, td{
    //   text-align: center;
    // }
  }
  .pagination {
    padding-top: 10px;
    display: flex;
    justify-content: flex-end;
    padding-right: 0;
  }
}
</style>
